package com.hanxiaozhang.threadbase1ndedition.no0interview;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TransferQueue;
import java.util.concurrent.locks.LockSupport;

/**
 * 〈一句话功能简述〉<br>
 * 〈两个线程打印 A1B2C3 ... Z26〉
 *
 * @author hanxinghua
 * @create 2021/11/14
 * @since 1.0.0
 */
public class No2 {

    static int letter = 65;
    static int number = 1;
    static int letter_1 = 65;
    static int number_1 = 1;
    static Thread t1 = null, t2 = null;


    public static void main(String[] args) throws InterruptedException {

        System.out.println("-----测试-----");
        System.out.println((int) 'A');
        System.out.println((char) 65);
        System.out.println((char) 49);


        System.out.println("-----使用CAS-----");
        No2Print1 no2Print1 = new No2Print1();
        new Thread(no2Print1::printLetter).start();
        new Thread(no2Print1::printNumber).start();

        TimeUnit.SECONDS.sleep(1);
        System.out.println();

        System.out.println("-----使用synchronized-1-----");
        No2Print2 no2Print2 = new No2Print2();
        new Thread(no2Print2::printLetter).start();
        new Thread(no2Print2::printNumber).start();

        TimeUnit.SECONDS.sleep(1);
        System.out.println();

        System.out.println("-----使用synchronized-2-----");
        No2Print3 no2Print3 = new No2Print3();
        new Thread(no2Print3::printLetter).start();
        new Thread(no2Print3::printNumber).start();

        TimeUnit.SECONDS.sleep(1);
        System.out.println();


        System.out.println("-----使用LockSupport-----");
        t1 = new Thread(() -> {
            for (int i = 0; i < 26; i++) {
                System.out.print((char) (letter++));
                LockSupport.unpark(t2);
                LockSupport.park();
            }
        });
        t2 = new Thread(() -> {
            for (int i = 0; i < 26; i++) {
                LockSupport.park();
                System.out.print(number++);
                System.out.print(" ");
                LockSupport.unpark(t1);

            }
        });

        t1.start();
        t2.start();

        TimeUnit.SECONDS.sleep(1);
        System.out.println();

        System.out.println("-----使用TransferQueue-----");
        TransferQueue<String> queue = new LinkedTransferQueue<>();
        new Thread(() -> {
            try {
                for (int i = 0; i < 26; i++) {
                    System.out.print(queue.take());
                    queue.transfer(String.valueOf(number_1++));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 26; i++) {
                    queue.transfer(String.valueOf((char) (letter_1++)));
                    System.out.print(queue.take());
                    System.out.print(" ");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();

    }

}

/**
 * 使用CAS
 */
class No2Print1 {

    enum ReadyToRun {T1, T2}

    private int letter = 65;
    private int number = 1;

    private volatile ReadyToRun r = ReadyToRun.T1;

    public void printLetter() {
        for (int i = 0; i < 26; i++) {
            while (r != ReadyToRun.T1) {
            }
            System.out.print((char) letter++);
            r = ReadyToRun.T2;
        }
    }

    public void printNumber() {
        for (int i = 0; i < 26; i++) {
            while (r != ReadyToRun.T2) {
            }
            System.out.print(number++);
            System.out.print(" ");
            r = ReadyToRun.T1;
        }
    }

}

/**
 * 使用synchronized
 */
class No2Print2 {

    private int letter = 65;
    private int number = 1;
    private int count = 0;

    final Object lock = new Object();

    public void printLetter() {
        synchronized (lock) {
            for (int i = 0; i < 26; i++) {
                while (count == 1) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print((char) letter);
                letter++;
                count++;
                lock.notify();
            }
            // 结束必须，notify(),因为有一方会wait()
            lock.notify();
        }
    }

    public void printNumber() {
        synchronized (lock) {
            for (int i = 0; i < 26; i++) {
                while (count == 0) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print(number);
                System.out.print(" ");
                number++;
                count--;
                lock.notify();
            }
            // 结束必须，notify(),因为有一方会wait()
            lock.notify();
        }
    }

}


/**
 * 使用synchronized
 */
class No2Print3 {

    private int letter = 65;
    private int number = 1;

    private CountDownLatch count = new CountDownLatch(1);

    final Object lock = new Object();

    public void printLetter() {
        synchronized (lock) {
            for (int i = 0; i < 26; i++) {
                System.out.print((char) letter++);
                count.countDown();
                lock.notify();
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            lock.notify();
        }
    }

    public void printNumber() {
        try {
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (lock) {
            for (int i = 0; i < 26; i++) {
                System.out.print(number++);
                System.out.print(" ");
                lock.notify();
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            lock.notify();
        }
    }
}
